package com.mr.security.web.commons.filter;

import com.mr.security.web.commons.config.SecurityPropertiesConfig;
import com.mr.security.web.commons.exception.ValidateCodeException;
import com.mr.security.web.commons.utils.Constants;
import com.mr.security.web.commons.utils.ValidateCodeEnum;
import com.mr.security.web.commons.validate.ValidateCodeProcessorHolder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * Class: ValidateCodeFilter
 * User (作者):MRui
 * TODO (描述)：图形验证过滤器（在登陆校验过滤器之前过滤）（OncePerRequestFilter只触发一次）
 * Date 2019-01-21 21:01
 */
@Slf4j
@Data
public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean {

    /** security验证码校验失败处理器 */
    private AuthenticationFailureHandler failureHandler;

    private Set<String> urls = new HashSet<>();
    /** 存放所有需要校验验证码的url */
    private Map<String, ValidateCodeEnum> urlMap = new HashMap<>();
    /** 系统配置信息 */
    private SecurityPropertiesConfig securityProperties;
    /** url工具类 */
    private AntPathMatcher pathMatcher = new AntPathMatcher();
    /** 系统中的校验码处理器 */
    @Autowired
    private ValidateCodeProcessorHolder validateCodeProcessorHolder;


    /**
     * User (作者): MRui
     * TODO (描述)：该方法实现InitializingBean会在初始化后在调用（初始化要拦截的url配置信息）
     * Data：2019-01-27 22:43
     */
    @Override
    public void afterPropertiesSet() throws ServletException{
        super.afterPropertiesSet();
        /* 初始化图形验证码 */
        urlMap.put(Constants.DEFAULT_LOGIN_PROCESSING_URL_FORM, ValidateCodeEnum.IMAGE);
        addUrlToMap(securityProperties.getImageCodeUrls(), ValidateCodeEnum.IMAGE);
        /* 初始化短信验证码 */
        urlMap.put(Constants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE, ValidateCodeEnum.SMS);
        addUrlToMap(securityProperties.getSmsCodeUrls(), ValidateCodeEnum.SMS);
    }

    /**
     * User (作者): MRui
     * TODO (描述)：将url添加到map中
     * Data：2019-01-27 22:42
     */
    protected void addUrlToMap(String urlString, ValidateCodeEnum type) {
        if (StringUtils.isNotBlank(urlString)) {
            String[] urls = StringUtils.splitByWholeSeparatorPreserveAllTokens(urlString, ",");
            for (String url : urls) {
                urlMap.put(url, type);
            }
        }
    }

    /**
     * User (作者): MRui
     * TODO (描述)：过滤器实现逻辑内容
     * Data：2019-01-27 23:02
     */
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        /* 获取校验码类型 */
        ValidateCodeEnum type = getValidateCodeType(httpServletRequest);
        if (type != null) {
            log.info("校验请求(" + httpServletRequest.getRequestURI() + ")中的验证码,验证码类型" + type);
            try {
                //校验session
                validateCodeProcessorHolder.findValidateCodeProcessor(type)
                        .validate(new ServletWebRequest(httpServletRequest, httpServletResponse));
                log.info("验证码校验通过");
            } catch (ValidateCodeException exception) {
                //调用失败处理器返回错误信息
                failureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, exception);
                return;
            }
        }
        //调用后面的过滤器
        filterChain.doFilter(httpServletRequest,httpServletResponse);
    }

    /**
     * User (作者): MRui
     * TODO (描述)：获取校验码的类型，如果当前请求不需要校验，则返回null
     * Data：2019-01-27 22:46
     */
    private ValidateCodeEnum getValidateCodeType(HttpServletRequest request) {
        ValidateCodeEnum result = null;
        if (!StringUtils.equalsIgnoreCase(request.getMethod(), "get")) {
            Set<String> urls = urlMap.keySet();
            for (String url : urls) {
                //任何一个url能匹配上,则做过滤
                if (pathMatcher.match(url, request.getRequestURI())) {
                    result = urlMap.get(url);
                }
            }
        }
        return result;
    }
}





